<?php

namespace App\Core;

use App\Constants\AppInfo;
use App\Core\MallLogger;
use App\Utils\LarkMessage;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\HttpClient\CurlHttpClient;

class AppErrorService
{

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var \Redis
     */
    private $redis;

    /**
     * @return LoggerInterface
     */
    public function getLogger(): LoggerInterface
    {
        return $this->logger;
    }

    /**
     * @param LoggerInterface $logger
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * @return \Redis
     */
    public function getRedis(): \Redis
    {
        return $this->redis;
    }

    /**
     * @param \RedisCluster $redis
     */
    public function setRedis(\RedisCluster $redis): void
    {
        $this->redis = $redis;
    }



    /**
     * @param string $message
     * @param array $context
     */
    public function errorLogNotice($message, $context)
    {
        if (!$this->whetherAbnormalContext($context)) {
            return;
        }
        $errorId = md5($message . $context['file'] . $context['line'] . $context['execption']);
        $fileCache = new FilesystemAdapter();
        $fileCache->get('ERROR_' . $errorId, function ($item) use ($message, $context) {
            /**@var CacheItem $item */
            try {
                $context['title'] = $message;
                $body = $this->conversionLarkMessageBody($context);
                $this->baseSendLark($body);
                $item->expiresAfter(HLL_IS_PRD ? 60 * 30 : 1);
            } catch (\Throwable $exception) {
                /** @var Logger $logger */
                $logger = $this->logger;
                $logger->warning($exception->getMessage(), ['trace' => $exception->getTraceAsString()]);
            }
            return 1;
        });
    }

    public function baseSendLark($message,$chatName = '创新业务部-程序报告群')
    {
        $chatName = '创新业务部-程序报告群';
        if (!HLL_IS_PRD) {
            $chatName = 'dev服务器问题';
        }
        $client = new CurlHttpClient();
        $client->setLogger($this->logger);
        $appid = 'cli_9f9ff9ea972dd00c';
        $appsecret = 'jBC6KE0UyGAEWExfKn07qdszAx50QOPe';
        $lark = new LarkMessage($appid, $appsecret);
        $lark->setClient($client);
        $lark->setRedis($this->redis);
        $chat = $lark->cacheChat($chatName);
        $lark->sendFeishuMsgCommon(LarkMessage::MESSAGE_TYPE_INTERACTIVE,
            ['chat_id' => $chat['chat_id']], $message);
    }

    public function conversionLarkMessageBody($message)
    {
        $env = HLL_ENV;
        $service = AppInfo::APPID;
        $time = date('Y-m-d H:i:s');
        /** @var MallLogger $logger */
        $logger = $this->logger;
        $traceNo = $logger->getTraceNo();
        $action = $logger->getHandlerAction();
        $title = "[{$env}] {$message['message']}";
        $body = [
            "config" => ["wide_screen_mode" => true],
            "elements" =>
                [
                    [
                        "fields" =>
                            [
                                [
                                    "is_short" => true,
                                    "text" => ["content" => "**时间**\n{$time}", "tag" => "lark_md"]
                                ],
                                ["is_short" => true, "text" => ["content" => "**环境**\n{$env} {$service}", "tag" => "lark_md"]]
                            ],
                        "tag" => "div"
                    ],
                    [
                        "fields" =>
                            [
                                [
                                    "is_short" => true,
                                    "text" => ["content" => "**串码**\n{$traceNo}", "tag" => "lark_md"]
                                ],
                                ["is_short" => true, "text" => ["content" => "**路由**\n{$action}", "tag" => "lark_md"]]
                            ],
                        "tag" => "div"
                    ],
                    [
                        "tag" => "div",
                        "text" => [
                            "content" => "{$message['message']} {$message['execption']}\n在{$message['file']}第{$message['line']}行\n
          {$message['trace']}", "tag" => "lark_md"
                        ]
                    ],
                    ["tag" => "hr"],
                    [
                        "elements" =>
                            [["content" => "[来自应急通知](https://www.open.feishu.cn/)", "tag" => "lark_md"]], "tag" => "note"
                    ]
                ], "header" =>
                [
                    "template" => "red",
                    "title" =>
                        ["content" => "{$title}", "tag" => "plain_text"]
                ]
        ];
        if (!HLL_IS_DEV) {
            $body['elements'][] = [
                "tag" => "div",
                "text" => [
                    "content" => "<at id=all>所有人<at>", "tag" => "lark_md"
                ]
            ];
        }
        return $body;
    }

    /**
     * @param \Throwable $exception
     * @return array {'file':'string'}
     */
    public static function formatExceptionObject($exception)
    {
        return [
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'trace' => $exception->getTraceAsString(),
            'execption' => get_class($exception)
        ];
    }

    /**
     * 检查是否是异常上下文
     * @param $context
     * @return bool
     */
    public function whetherAbnormalContext($context)
    {
        $keys = ['file', 'line', 'message', 'code', 'trace', 'execption'];
        foreach ($keys as $key) {
            if (!in_array($key, array_keys($context))) {
                return false;
            }
        }
        return true;
    }
}